page.tsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. "use client";
  2. import { fetchApi, fetchFile } from "@/app/_modules/func";
  3. import {
  4. DeleteOutlined,
  5. ExclamationCircleFilled,
  6. PlusOutlined,
  7. ReloadOutlined,
  8. } from "@ant-design/icons";
  9. import type {
  10. ActionType,
  11. ProColumns,
  12. ProFormInstance,
  13. } from "@ant-design/pro-components";
  14. import {
  15. ModalForm,
  16. PageContainer,
  17. ProForm,
  18. ProFormDigit,
  19. ProFormRadio,
  20. ProFormSelect,
  21. ProFormText,
  22. ProFormTextArea,
  23. ProTable,
  24. } from "@ant-design/pro-components";
  25. import { Button, message, Modal, Space, Tag } from "antd";
  26. import { useRouter } from "next/navigation";
  27. import {
  28. faCheck,
  29. faDownload,
  30. faPenToSquare,
  31. faToggleOff,
  32. faToggleOn,
  33. faXmark,
  34. } from "@fortawesome/free-solid-svg-icons";
  35. import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
  36. import { useRef, useState } from "react";
  37. //查询类型详情
  38. const queryTypeAPI = "/api/system/dict/type";
  39. //查询所有类型列表
  40. const queryTypeListAPI = "/api/system/dict/type/optionselect";
  41. //查询表格数据API
  42. const queryAPI = "/api/system/dict/data/list";
  43. //新建数据API
  44. const newAPI = "/api/system/dict/data";
  45. //修改数据API
  46. const modifyAPI = "/api/system/dict/data";
  47. //查询详情数据API
  48. const queryDetailAPI = "/api/system/dict/data";
  49. //删除API
  50. const deleteAPI = "/api/system/dict/data";
  51. //导出API
  52. const exportAPI = "/api/system/dict/data/export";
  53. //导出文件前缀名
  54. const exportFilePrefix = "data";
  55. export default function DictData({ params }: { params: { dictid: string } }) {
  56. const { push } = useRouter();
  57. const [defaultType, setDefaultType] = useState("");
  58. //获取对应的字典类型的值
  59. const getTypeData = async () => {
  60. const resp = await fetchApi(`${queryTypeAPI}/${params.dictid}`, push);
  61. if (resp != undefined) {
  62. if (searchTableFormRef.current) {
  63. searchTableFormRef.current.setFieldsValue({
  64. dictType: resp.data.dictType,
  65. });
  66. }
  67. setDefaultType(resp.data.dictType);
  68. return resp.data.dictType;
  69. }
  70. return "";
  71. };
  72. //查询字典类型列表
  73. const getTypeList = async () => {
  74. const dataArray: Array<any> = new Array<any>();
  75. const resp = await fetchApi(queryTypeListAPI, push);
  76. if (resp != undefined) {
  77. resp.data.forEach((item: any) => {
  78. const type = {
  79. label: item.dictName,
  80. value: item.dictType,
  81. };
  82. dataArray.push(type);
  83. });
  84. }
  85. return dataArray;
  86. };
  87. //表格列定义
  88. const columns: ProColumns[] = [
  89. {
  90. title: "字典名称",
  91. dataIndex: "dictType",
  92. valueType: "select",
  93. fieldProps: {
  94. allowClear: false,
  95. },
  96. request: getTypeList,
  97. hideInTable: true,
  98. order: 3,
  99. },
  100. {
  101. title: "数据编码",
  102. dataIndex: "dictCode",
  103. search: false,
  104. },
  105. {
  106. title: "数据标签",
  107. fieldProps: {
  108. placeholder: "请输入数据标签",
  109. },
  110. dataIndex: "dictLabel",
  111. order: 2,
  112. render: (_, record) => {
  113. const isTag = record.listClass === "";
  114. let tagColor = "default";
  115. if (record.listClass === "") {
  116. return _;
  117. } else {
  118. switch (record.listClass) {
  119. case "default":
  120. tagColor = "processing";
  121. break;
  122. case "primary":
  123. tagColor = "processing";
  124. break;
  125. case "success":
  126. tagColor = "success";
  127. break;
  128. case "info":
  129. tagColor = "default";
  130. break;
  131. case "warning":
  132. tagColor = "warning";
  133. break;
  134. case "danger":
  135. tagColor = "error";
  136. break;
  137. default:
  138. tagColor = "processing";
  139. break;
  140. }
  141. return (
  142. <Space>
  143. <Tag color={tagColor}>{_}</Tag>
  144. </Space>
  145. );
  146. }
  147. },
  148. },
  149. {
  150. title: "数据键值",
  151. dataIndex: "dictValue",
  152. search: false,
  153. },
  154. {
  155. title: "数据排序",
  156. dataIndex: "dictSort",
  157. sorter: true,
  158. search: false,
  159. },
  160. {
  161. title: "状态",
  162. fieldProps: {
  163. placeholder: "请选择数据状态",
  164. },
  165. dataIndex: "status",
  166. valueType: "select",
  167. render: (_, record) => {
  168. return (
  169. <Space>
  170. <Tag
  171. color={record.status === "0" ? "green" : "red"}
  172. icon={
  173. record.status == 0 ? (
  174. <FontAwesomeIcon icon={faCheck} />
  175. ) : (
  176. <FontAwesomeIcon icon={faXmark} />
  177. )
  178. }
  179. >
  180. {_}
  181. </Tag>
  182. </Space>
  183. );
  184. },
  185. valueEnum: {
  186. 0: {
  187. text: "正常",
  188. status: "0",
  189. },
  190. 1: {
  191. text: "停用",
  192. status: "1",
  193. },
  194. },
  195. order: 1,
  196. },
  197. {
  198. title: "备注",
  199. dataIndex: "remark",
  200. search: false,
  201. },
  202. {
  203. title: "创建时间",
  204. dataIndex: "createTime",
  205. valueType: "dateTime",
  206. search: false,
  207. },
  208. {
  209. title: "操作",
  210. key: "option",
  211. search: false,
  212. render: (_, record) => [
  213. <Button
  214. key="modifyBtn"
  215. type="link"
  216. icon={<FontAwesomeIcon icon={faPenToSquare} />}
  217. onClick={() => onClickShowRowModifyModal(record)}
  218. >
  219. 修改
  220. </Button>,
  221. <Button
  222. key="deleteBtn"
  223. type="link"
  224. danger
  225. icon={<DeleteOutlined />}
  226. onClick={() => onClickDeleteRow(record)}
  227. >
  228. 删除
  229. </Button>,
  230. ],
  231. },
  232. ];
  233. //0.查询表格数据
  234. const queryTableData = async (params: any, sorter: any, filter: any) => {
  235. const searchParams = {
  236. pageNum: params.current,
  237. ...params,
  238. };
  239. delete searchParams.current;
  240. const queryParams = new URLSearchParams(searchParams);
  241. //如果没有带上默认的字典类型,查询绑定上
  242. if (!("dictType" in searchParams)) {
  243. const defaultType = await getTypeData();
  244. queryParams.append("dictType", defaultType);
  245. }
  246. Object.keys(sorter).forEach((key) => {
  247. queryParams.append("orderByColumn", key);
  248. if (sorter[key] === "ascend") {
  249. queryParams.append("isAsc", "ascending");
  250. } else {
  251. queryParams.append("isAsc", "descending");
  252. }
  253. });
  254. const body = await fetchApi(`${queryAPI}?${queryParams}`, push);
  255. return body;
  256. };
  257. //1.新建
  258. //确定新建数据
  259. const executeAddData = async (values: any) => {
  260. const body = await fetchApi(newAPI, push, {
  261. method: "POST",
  262. headers: {
  263. "Content-Type": "application/json",
  264. },
  265. body: JSON.stringify(values),
  266. });
  267. if (body != undefined) {
  268. if (body.code == 200) {
  269. message.success(body.msg);
  270. if (actionTableRef.current) {
  271. actionTableRef.current.reload();
  272. }
  273. return true;
  274. }
  275. message.error(body.msg);
  276. return false;
  277. }
  278. return false;
  279. };
  280. //2.修改
  281. //是否展示修改对话框
  282. const [isShowModifyDataModal, setIsShowModifyDataModal] = useState(false);
  283. //展示修改对话框
  284. const onClickShowRowModifyModal = (record?: any) => {
  285. queryRowData(record);
  286. setIsShowModifyDataModal(true);
  287. };
  288. //修改数据表单引用
  289. const modifyFormRef = useRef<ProFormInstance>();
  290. //操作当前数据的附加数据
  291. const [operatRowData, setOperateRowData] = useState<{
  292. [key: string]: any;
  293. }>({});
  294. //查询并加载待修改数据的详细信息
  295. const queryRowData = async (record?: any) => {
  296. const dictCode =
  297. record !== undefined ? record.dictCode : selectedRow.dictCode;
  298. operatRowData["dictCode"] = dictCode;
  299. setOperateRowData(operatRowData);
  300. if (dictCode !== undefined) {
  301. const body = await fetchApi(`${queryDetailAPI}/${dictCode}`, push);
  302. if (body !== undefined) {
  303. if (body.code == 200) {
  304. modifyFormRef?.current?.setFieldsValue({
  305. //需要加载到修改表单中的数据
  306. dictType: body.data.dictType,
  307. dictLabel: body.data.dictLabel,
  308. dictValue: body.data.dictValue,
  309. dictSort: body.data.dictSort,
  310. status: body.data.status,
  311. listClass: body.data.listClass,
  312. cssClass: body.data.cssClass,
  313. remark: body.data.remark,
  314. });
  315. }
  316. }
  317. }
  318. };
  319. //确认修改数据
  320. const executeModifyData = async (values: any) => {
  321. values["dictCode"] = operatRowData["dictCode"];
  322. const body = await fetchApi(modifyAPI, push, {
  323. method: "PUT",
  324. headers: {
  325. "Content-Type": "application/json",
  326. },
  327. body: JSON.stringify(values),
  328. });
  329. if (body !== undefined) {
  330. if (body.code == 200) {
  331. message.success(body.msg);
  332. //刷新列表
  333. if (actionTableRef.current) {
  334. actionTableRef.current.reload();
  335. }
  336. setIsShowModifyDataModal(false);
  337. return true;
  338. }
  339. message.error(body.msg);
  340. return false;
  341. }
  342. };
  343. //3.删除
  344. //点击删除按钮,展示删除确认框
  345. const onClickDeleteRow = (record?: any) => {
  346. const dictCode =
  347. record != undefined ? record.dictCode : selectedRowKeys.join(",");
  348. Modal.confirm({
  349. title: "系统提示",
  350. icon: <ExclamationCircleFilled />,
  351. content: `确定删除字典编码为“${dictCode}”的数据项?`,
  352. onOk() {
  353. executeDeleteRow(dictCode);
  354. },
  355. onCancel() {},
  356. });
  357. };
  358. //确定删除选中的数据
  359. const executeDeleteRow = async (dictCode: any) => {
  360. const body = await fetchApi(`${deleteAPI}/${dictCode}`, push, {
  361. method: "DELETE",
  362. });
  363. if (body !== undefined) {
  364. if (body.code == 200) {
  365. message.success("删除成功");
  366. //修改按钮变回不可点击
  367. setRowCanModify(false);
  368. //删除按钮变回不可点击
  369. setRowCanDelete(false);
  370. //选中行数据重置为空
  371. setSelectedRowKeys([]);
  372. //刷新列表
  373. if (actionTableRef.current) {
  374. actionTableRef.current.reload();
  375. }
  376. } else {
  377. message.error(body.msg);
  378. }
  379. }
  380. };
  381. //4.导出
  382. //导出表格数据
  383. const exportTable = async () => {
  384. if (searchTableFormRef.current) {
  385. const formData = new FormData();
  386. const data = {
  387. pageNum: page,
  388. pageSize: pageSize,
  389. ...searchTableFormRef.current.getFieldsValue(),
  390. };
  391. Object.keys(data).forEach((key) => {
  392. if (data[key] !== undefined) {
  393. formData.append(key, data[key]);
  394. }
  395. });
  396. await fetchFile(
  397. exportAPI,
  398. push,
  399. {
  400. method: "POST",
  401. body: formData,
  402. },
  403. `${exportFilePrefix}_${new Date().getTime()}.xlsx`
  404. );
  405. }
  406. };
  407. //5.选择行
  408. //选中行操作
  409. const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  410. const [selectedRow, setSelectedRow] = useState(undefined as any);
  411. //修改按钮是否可用,选中行时才可用
  412. const [rowCanModify, setRowCanModify] = useState(false);
  413. //删除按钮是否可用,选中行时才可用
  414. const [rowCanDelete, setRowCanDelete] = useState(false);
  415. //ProTable rowSelection
  416. const rowSelection = {
  417. onChange: (newSelectedRowKeys: React.Key[], selectedRows: any[]) => {
  418. setSelectedRowKeys(newSelectedRowKeys);
  419. setRowCanDelete(newSelectedRowKeys && newSelectedRowKeys.length > 0);
  420. if (newSelectedRowKeys && newSelectedRowKeys.length == 1) {
  421. setSelectedRow(selectedRows[0]);
  422. setRowCanModify(true);
  423. } else {
  424. setRowCanModify(false);
  425. setSelectedRow(undefined);
  426. }
  427. },
  428. //复选框的额外禁用判断
  429. // getCheckboxProps: (record) => ({
  430. // disabled: record.userId == 1,
  431. // }),
  432. };
  433. //搜索栏显示状态
  434. const [showSearch, setShowSearch] = useState(true);
  435. //action对象引用
  436. const actionTableRef = useRef<ActionType>();
  437. //搜索表单对象引用
  438. const searchTableFormRef = useRef<ProFormInstance>();
  439. //当前页数和每页条数
  440. const [page, setPage] = useState(1);
  441. const defaultPageSize = 10;
  442. const [pageSize, setPageSize] = useState(defaultPageSize);
  443. const pageChange = (page: number, pageSize: number) => {
  444. setPage(page);
  445. setPageSize(pageSize);
  446. };
  447. return (
  448. <PageContainer
  449. header={{
  450. title: "字典数据",
  451. onBack(e) {
  452. push("/system/dict");
  453. },
  454. }}
  455. >
  456. <ProTable
  457. formRef={searchTableFormRef}
  458. rowKey="dictCode"
  459. rowSelection={{
  460. selectedRowKeys,
  461. ...rowSelection,
  462. }}
  463. columns={columns}
  464. request={async (params: any, sorter: any, filter: any) => {
  465. // 表单搜索项会从 params 传入,传递给后端接口。
  466. const data = await queryTableData(params, sorter, filter);
  467. if (data !== undefined) {
  468. return Promise.resolve({
  469. data: data.rows,
  470. success: true,
  471. total: data.total,
  472. });
  473. }
  474. return Promise.resolve({
  475. data: [],
  476. success: true,
  477. });
  478. }}
  479. pagination={{
  480. defaultPageSize: defaultPageSize,
  481. showQuickJumper: true,
  482. showSizeChanger: true,
  483. onChange: pageChange,
  484. }}
  485. search={
  486. showSearch
  487. ? {
  488. defaultCollapsed: false,
  489. searchText: "搜索",
  490. }
  491. : false
  492. }
  493. dateFormatter="string"
  494. actionRef={actionTableRef}
  495. toolbar={{
  496. actions: [
  497. <ModalForm
  498. key="addmodal"
  499. title="添加字典数据"
  500. trigger={
  501. <Button icon={<PlusOutlined />} type="primary">
  502. 新建
  503. </Button>
  504. }
  505. autoFocusFirstInput
  506. modalProps={{
  507. destroyOnHidden: true,
  508. }}
  509. submitTimeout={2000}
  510. onFinish={executeAddData}
  511. >
  512. <ProForm.Group>
  513. <ProFormText
  514. width="md"
  515. name="dictType"
  516. label="字典类型"
  517. initialValue={defaultType}
  518. disabled
  519. />
  520. </ProForm.Group>
  521. <ProForm.Group>
  522. <ProFormText
  523. width="md"
  524. name="dictLabel"
  525. label="数据标签"
  526. rules={[{ required: true, message: "请输入数据标签" }]}
  527. />
  528. <ProFormText
  529. width="md"
  530. name="dictValue"
  531. label="数据键值"
  532. rules={[{ required: true, message: "请输入数据键值" }]}
  533. />
  534. </ProForm.Group>
  535. <ProForm.Group>
  536. <ProFormDigit
  537. fieldProps={{ precision: 0 }}
  538. width="md"
  539. name="dictSort"
  540. initialValue="0"
  541. label="数据排序"
  542. placeholder="请输入数据排序"
  543. rules={[{ required: true, message: "请输入数据排序" }]}
  544. />
  545. <ProFormRadio.Group
  546. width="md"
  547. name="status"
  548. label="状态"
  549. initialValue="0"
  550. options={[
  551. {
  552. label: "正常",
  553. value: "0",
  554. },
  555. {
  556. label: "停用",
  557. value: "1",
  558. },
  559. ]}
  560. />
  561. </ProForm.Group>
  562. <ProForm.Group>
  563. <ProFormSelect
  564. width="md"
  565. name="listClass"
  566. label="回显样式"
  567. valueEnum={{
  568. default: {
  569. text: "默认(default)",
  570. status: "default",
  571. },
  572. primary: {
  573. text: "主要(primary)",
  574. status: "primary",
  575. },
  576. success: {
  577. text: "成功(成功)",
  578. status: "success",
  579. },
  580. info: {
  581. text: "信息(info)",
  582. status: "info",
  583. },
  584. warning: {
  585. text: "警告(warning)",
  586. status: "warning",
  587. },
  588. danger: {
  589. text: "危险(danger)",
  590. status: "danger",
  591. },
  592. }}
  593. />
  594. <ProFormText width="md" name="cssClass" label="样式属性" />
  595. </ProForm.Group>
  596. <ProFormTextArea
  597. name="remark"
  598. width={688}
  599. label="备注"
  600. placeholder="请输入内容"
  601. />
  602. </ModalForm>,
  603. <ModalForm
  604. key="modifymodal"
  605. title="修改岗位"
  606. formRef={modifyFormRef}
  607. trigger={
  608. <Button
  609. icon={<FontAwesomeIcon icon={faPenToSquare} />}
  610. disabled={!rowCanModify}
  611. onClick={() => onClickShowRowModifyModal()}
  612. >
  613. 修改
  614. </Button>
  615. }
  616. open={isShowModifyDataModal}
  617. autoFocusFirstInput
  618. modalProps={{
  619. destroyOnHidden: true,
  620. onCancel: () => {
  621. setIsShowModifyDataModal(false);
  622. },
  623. }}
  624. submitTimeout={2000}
  625. onFinish={executeModifyData}
  626. >
  627. <ProForm.Group>
  628. <ProFormText
  629. width="md"
  630. name="dictType"
  631. label="字典类型"
  632. disabled
  633. />
  634. </ProForm.Group>
  635. <ProForm.Group>
  636. <ProFormText
  637. width="md"
  638. name="dictLabel"
  639. label="字典标签"
  640. rules={[{ required: true, message: "请输入字典标签" }]}
  641. />
  642. <ProFormText
  643. width="md"
  644. name="dictValue"
  645. label="字典键值"
  646. rules={[{ required: true, message: "请输入字典键值" }]}
  647. />
  648. </ProForm.Group>
  649. <ProForm.Group>
  650. <ProFormDigit
  651. fieldProps={{ precision: 0 }}
  652. width="md"
  653. name="dictSort"
  654. initialValue="0"
  655. label="显示排序"
  656. placeholder="请输入显示排序"
  657. rules={[{ required: true, message: "请输入显示排序" }]}
  658. />
  659. <ProFormRadio.Group
  660. width="md"
  661. name="status"
  662. label="状态"
  663. initialValue="0"
  664. options={[
  665. {
  666. label: "正常",
  667. value: "0",
  668. },
  669. {
  670. label: "停用",
  671. value: "1",
  672. },
  673. ]}
  674. />
  675. </ProForm.Group>
  676. <ProForm.Group>
  677. <ProFormSelect
  678. width="md"
  679. name="listClass"
  680. label="回显样式"
  681. valueEnum={{
  682. default: {
  683. text: "默认(default)",
  684. status: "default",
  685. },
  686. primary: {
  687. text: "主要(primary)",
  688. status: "primary",
  689. },
  690. success: {
  691. text: "成功(成功)",
  692. status: "success",
  693. },
  694. info: {
  695. text: "信息(info)",
  696. status: "info",
  697. },
  698. warning: {
  699. text: "警告(warning)",
  700. status: "warning",
  701. },
  702. danger: {
  703. text: "危险(danger)",
  704. status: "danger",
  705. },
  706. }}
  707. />
  708. <ProFormText width="md" name="cssClass" label="样式属性" />
  709. </ProForm.Group>
  710. <ProFormTextArea
  711. name="remark"
  712. width={688}
  713. label="备注"
  714. placeholder="请输入内容"
  715. />
  716. </ModalForm>,
  717. <Button
  718. key="danger"
  719. danger
  720. icon={<DeleteOutlined />}
  721. disabled={!rowCanDelete}
  722. onClick={() => onClickDeleteRow()}
  723. >
  724. 删除
  725. </Button>,
  726. <Button
  727. key="export"
  728. type="primary"
  729. icon={<FontAwesomeIcon icon={faDownload} />}
  730. onClick={exportTable}
  731. >
  732. 导出
  733. </Button>,
  734. ],
  735. settings: [
  736. {
  737. key: "switch",
  738. icon: showSearch ? (
  739. <FontAwesomeIcon icon={faToggleOn} />
  740. ) : (
  741. <FontAwesomeIcon icon={faToggleOff} />
  742. ),
  743. tooltip: showSearch ? "隐藏搜索栏" : "显示搜索栏",
  744. onClick: (key: string | undefined) => {
  745. setShowSearch(!showSearch);
  746. },
  747. },
  748. {
  749. key: "refresh",
  750. tooltip: "刷新",
  751. icon: <ReloadOutlined />,
  752. onClick: (key: string | undefined) => {
  753. if (actionTableRef.current) {
  754. actionTableRef.current.reload();
  755. }
  756. },
  757. },
  758. ],
  759. }}
  760. />
  761. </PageContainer>
  762. );
  763. }